package org.eredlab.g4.arm.web;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.eredlab.g4.arm.service.MonitorService;
import org.eredlab.g4.arm.service.OrganizationService;
import org.eredlab.g4.arm.util.idgenerator.IDHelper;
import org.eredlab.g4.arm.vo.UserInfoVo;
import org.eredlab.g4.ccl.datastructure.Dto;
import org.eredlab.g4.ccl.datastructure.impl.BaseDto;
import org.eredlab.g4.ccl.util.G4Utils;
import org.eredlab.g4.rif.report.fcf.FcfDataMapper;
import org.eredlab.g4.rif.report.fcf.GraphConfig;
import org.eredlab.g4.rif.report.fcf.Set;
import org.eredlab.g4.rif.util.SessionListener;
import org.eredlab.g4.rif.util.WebUtils;
import org.eredlab.g4.rif.web.BaseAction;

/**
 * 登录页面Action
 * 
 * @author XiongChun
 * @since 2010-01-13
 * @see BaseAction
 */
public class LoginAction extends BaseAction {

	private static Log log = LogFactory.getLog(LoginAction.class);

	private OrganizationService organizationService = (OrganizationService) super.getService("organizationService");

	private MonitorService monitorService = (MonitorService) super.getService("monitorService");

	/**
	 * 登陆页面初始化
	 * 
	 * @param
	 * @return
	 */
	public ActionForward init(ActionMapping mapping, ActionForm form, HttpServletRequest request,
			HttpServletResponse response) throws Exception 
	{
		//由于最先启动的是应用服务器的监听器，在监听器中已经将系统的启动参数保存在一个集合中，并存放在应用程序作用于中共享数据！
		String bannerPath = getParamValue("LOGIN_WINDOW_BANNER", request);
		//将登陆的背景图片路径拼接完整！
		bannerPath = request.getContextPath() + bannerPath;
		//保存登录的背景图片地址以及登录标题！
		request.setAttribute("bannerPath", bannerPath);
		request.setAttribute("sysTitle", getParamValue("SYS_TITLE", request));
		//导向登录页面展示刚才设定的信息！
		return mapping.findForward("loginView");
	}

	/**
	 * 登陆身份验证
	 * 
	 * @param
	 * @return
	 */
	public ActionForward login(ActionMapping mapping, ActionForm form, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		String account = request.getParameter("account");
		String password = request.getParameter("password");
		password = G4Utils.encryptBasedDes(password);
		log.info("帐户[" + account + "]正尝试登陆系统...");
		Dto dto = new BaseDto();
		dto.put("account", account);
		Dto outDto = organizationService.getUserInfo(dto);
		//取出数据库中同当前用户账号相同的实体信息！而并不是直接通过用户名和密码来并列查询的！后面通过比较值是否相等来判断是否是有效用户！
		UserInfoVo userInfo = (UserInfoVo) outDto.get("userInfo");
		//System.out.println("破解密码："+G4Utils.decryptBasedDes(userInfo.getPassword()));
		//定义集合，用于展示登录的状态，是失败还是成功等等，相当于提示状态信息！
		Dto jsonDto = new BaseDto();
		//为空说明连这个账户都没有，因此提示账户有误而不是密码有误！
		if (G4Utils.isEmpty(userInfo))
		{
			jsonDto.put("success", new Boolean(false));
			jsonDto.put("msg", "帐号输入错误,请重新输入!");
			jsonDto.put("errorType", "1");
			log.warn("帐户[" + account + "]登陆失败.(失败原因：不存在此帐户)");
			write(jsonDto.toJson(), response);
			return mapping.findForward("");
		}
		//如果信息实体部位空说明有这个账户，因此接下来需要判断密码是否相等！
		if (!password.equals(userInfo.getPassword())) 
		{
			jsonDto.put("success", new Boolean(false));
			jsonDto.put("msg", "密码输入错误,请重新输入!");
			jsonDto.put("errorType", "2");
			log.warn(userInfo.getUsername() + "[" + userInfo.getAccount() + "]" + "登录系统失败(失败原因：密码输入错误)");
			write(jsonDto.toJson(), response);
			return mapping.findForward("");
		}
		
		
		
		
		
		//密码相等后只能说明这个登录用户是有效的，但并不是有效的就允许登录，还需要判断登录场景以及系统的默认设置情况来决定是否允许登录！
		
		//分别有二种情况，第一种是：是否允许同一个账户同时登录操作；第二种是：是否允许不同账户在同一客户机上登录，也就是说一个客户机是否只能运行一个登录用户！
		
		
		
		
		
		
		//从系统参数表中取得参数名为multi_session的值，用于判别是否可以同时登录！
		String multiSession = WebUtils.getParamValue("MULTI_SESSION", request);
		//不允许同时登录
		if ("0".equals(multiSession)) 
		{
			//在会话记录表中查询，同一个账户登录在线的个数！根据账户名来统计的！
			Integer sessions = (Integer) g4Reader.queryForObject("Organization.countHttpSessions", account);
			//如果大于0说明已经有登录的了！
			if (sessions.intValue() > 0) 
			{
				jsonDto.put("success", new Boolean(false));
				jsonDto.put("msg", "此用户已经登录,系统不允许建立多个会话连接!");
				jsonDto.put("errorType", "3");
				log.warn(userInfo.getUsername() + "[" + userInfo.getAccount() + "]"
						+ "登录系统失败(失败原因：此用户已经登录,系统参数配置为不允许一个用户建立多个连接)");
				write(jsonDto.toJson(), response);
				return mapping.findForward("");
			}
		}
		
		
		
		//能走到下面说明允许登录多个，但登录过个又分一种情况，那就是一个客户机上是否允许同时登录2个不同的用户！
		
		
		
		
		//将当前的会话的id赋给当前登录的用户！
		userInfo.setSessionID(request.getSession().getId());
		//会话创建的时间！
		userInfo.setSessionCreatedTime(G4Utils.getCurrentTime());
		//将当前的机子ip赋给当前用户！
		userInfo.setLoginIP(request.getRemoteAddr());
		//同样的设置当前的浏览器类型！
		userInfo.setExplorer(G4Utils.getClientExplorerType(request));
		//如果整体为真代表不是同一个账户，而是不同的账户试图在同一台电脑上登录！是不允许的！
		if (!checkMultiUser(userInfo, request)) 
		{
			jsonDto.put("success", new Boolean(false));
			jsonDto.put("msg", "不允许在同一客户端上同时以不同帐户登录系统,请先退出你已经登录的帐户后再尝试登录!");
			jsonDto.put("errorType", "1");
			log.warn("帐户[" + account + "]登陆失败.(失败原因：不允许在同一客户端上同时以不同帐户登录系统.请先退出你已经登录的帐户后再尝试登录)");
			write(jsonDto.toJson(), response);
			return mapping.findForward("");
		}
		//保存当前登录合格的用户信息到会话中并对用于进行监听！
		super.getSessionContainer(request).setUserInfo(userInfo);
		log.info(userInfo.getUsername() + "[" + userInfo.getAccount() + "]" + "成功登录系统!创建了一个有效Session连接,会话ID:["
				+ request.getSession().getId() + "]" + G4Utils.getCurrentTime());
		
		
		
		//如果当前会话有记录，那么就不添加会话消息，否则添加，因此总是只有一个，即会话的id是唯一的保存！
		SessionListener.addSession(request.getSession(), userInfo); // 保存有效Session
		
		
		
		
		
		
		//如果请求跟踪开启了就保存登录记录！
		if (pHelper.getValue("requestMonitor", "0").equals("1"))
		{
			saveLoginEvent(userInfo, request);
		}
		jsonDto.put("success", new Boolean(true));
		//将用户的id保存起来！
		jsonDto.put("userid", userInfo.getUserid());
		write(jsonDto.toJson(), response);
		return mapping.findForward("");
	}
	
	/**
	 * 回写Cookie
	 * @param userInfoVo
	 * @param request
	 */
	private void writeCookie2Client(UserInfoVo userInfoVo, HttpServletRequest request){
		
	}

	/**
	 * 不允许在同一客户端上不同帐户同时以同种浏览器登录系统<br>
	 * 规避同一客户端同时以不同账户使用同种浏览器登录系统窜账户的情况
	 * 
	 * @return
	 */
	//通过账户和ip共同判断的！其实只用ip来判断也是可以的！
	private Boolean checkMultiUser(UserInfoVo userInfoVo, HttpServletRequest request) 
	{
		boolean allowLogin = true;
		Cookie[] c=request.getCookies();
		for(Cookie cc:c)
		{
			System.out.println("Cookie名字："+cc.getName()+"\t\t\tCookie值："+cc.getValue());
		}
		String cookieUserid = WebUtils.getCookieValue(request.getCookies(), "eredg4.login.userid", "");
		//得到当前会话的id
		String sessionid = request.getSession().getId();
		//每次登录一个有效的用户就会在会话监听器的静态集合中添加一个以会话Id为键以会话对象为值的键值对！
		HttpSession httpSession = SessionListener.getSessionByID(sessionid);
		//如果在集合中没有找到这个会话，则说明这个会话已经结束或者并没有建立有效的会话即没登陆上！
		if (G4Utils.isNotEmpty(httpSession)) 
		{
			//UserInfoVo oldUserInfoVo = WebUtils.getSessionContainer(httpSession).getUserInfo();
			//如果不相同说明不是同一个账号，那么就不允许登录！否则可以登录！即一个账户可以登录多次！
			if (!cookieUserid.equals(userInfoVo.getUserid())) {
				allowLogin = false;
			}
		}
		return new Boolean(allowLogin);
	}

	/**
	 * 退出登录
	 * 
	 * @param
	 * @return
	 */
	public ActionForward logout(ActionMapping mapping, ActionForm form, HttpServletRequest request,
			HttpServletResponse response) throws Exception {
		UserInfoVo userInfo = super.getSessionContainer(request).getUserInfo();
		if (G4Utils.isNotEmpty(userInfo)) 
		{
			if (pHelper.getValue("requestMonitor", "0").equals("1"))
			{
				saveLogoutEvent(userInfo, request);
			}
			log.info(userInfo.getUsername() + "退出了系统!");
			//释放被监听的用户实体！
			super.getSessionContainer(request).setUserInfo(null);
		}
		if (G4Utils.isNotEmpty(request.getSession())) 
		{
			//如果会话不为空就强制会话失效！
			request.getSession().invalidate();
		}
		//重新导向登录页面！
		init(mapping, form, request, response);
		return mapping.findForward("loginView");
	}

	/**
	 * 保存登录事件
	 * 
	 * @param userInfo
	 */
	//又要要保存的时间包含了请求方面的内容，因此在保存事件时需要request对象！
	private void saveLoginEvent(UserInfoVo userInfo, HttpServletRequest request) 
	{
		Dto dto = new BaseDto();
		dto.put("account", userInfo.getAccount());
		dto.put("activetime", G4Utils.getCurrentTime());
		dto.put("userid", userInfo.getUserid());
		dto.put("username", userInfo.getUsername());
		dto.put("description", "登录系统");
		dto.put("requestpath", request.getRequestURI());
		dto.put("methodname", request.getParameter("reqCode"));
		dto.put("eventid", IDHelper.getEventID());
		//每次登录成功后都会记录登录的事件！
		monitorService.saveEvent(dto);
	}

	/**
	 * 保存退出事件
	 * 
	 * @param userInfo
	 */
	private void saveLogoutEvent(UserInfoVo userInfo, HttpServletRequest request) {
		Dto dto = new BaseDto();
		dto.put("account", userInfo.getAccount());
		dto.put("activetime", G4Utils.getCurrentTime());
		dto.put("userid", userInfo.getUserid());
		dto.put("username", userInfo.getUsername());
		dto.put("description", "退出系统");
		dto.put("requestpath", request.getRequestURI());
		dto.put("methodname", request.getParameter("reqCode"));
		dto.put("eventid", IDHelper.getEventID());
		//每次的退出也会记录时间！
		monitorService.saveEvent(dto);
	}
}
//注意事件表中的信息不同因为系统的重新启动而删除，除非手动去删除，否则系统不会自动去删除，但会话信息表会在每次启动之前删除！